Meisterdage TypeScripti veakäsitlust tüübiohutuse mustritega. Õppige looma robustseid rakendusi kohandatud vigade, tüübikaitsete ja tulemi monaadide abil, et saavutada ennustatav ja hooldatav kood.
TypeScripti veakäsitlus: erindite tüübiohutuse mustrid
Tarkvaraarenduse maailmas, kus rakendused on kõige aluseks alates globaalsetest finantssüsteemidest kuni igapäevaste mobiilsete interaktsioonideni, ei ole vastupidavate ja tõrketaluvate süsteemide ehitamine lihtsalt parim praktika – see on fundamentaalne vajadus. Kuigi JavaScript pakub dünaamilist ja paindlikku keskkonda, võib selle lõtv tüüpimine mõnikord viia käitusaja üllatusteni, eriti vigade käsitlemisel. Siin tuleb mängu TypeScript, mis toob esiplaanile staatilise tüübikontrolli ja pakub võimsaid tööriistu koodi ennustatavuse ja hooldatavuse parandamiseks.
Veakäsitlus on iga robustse rakenduse kriitiline aspekt. Ilma selge strateegiata võivad ootamatud probleemid viia ettearvamatu käitumise, andmete rikkumise või isegi täieliku süsteemi tõrkeni. Kombineerituna TypeScripti tüübiohutusega muutub veakäsitlus kaitsva kodeerimise tüütust kohustusest struktureeritud, ennustatavaks ja hallatavaks osaks teie rakenduse arhitektuurist.
See põhjalik juhend süveneb TypeScripti veakäsitluse nüanssidesse, uurides erinevaid mustreid ja parimaid praktikaid erindite tüübiohutuse tagamiseks. Me liigume kaugemale põhilistest try...catch plokkidest, avastades, kuidas TypeScripti funktsioone ära kasutada vigade defineerimiseks, püüdmiseks ja käsitlemiseks enneolematu täpsusega. Olenemata sellest, kas ehitate keerulist ettevõtterakendust, suure liiklusega veebiteenust või tipptasemel esirakenduse kogemust, annab nende mustrite mõistmine teile võimekuse kirjutada usaldusväärsemat, paremini silutavat ja hooldatavat koodi globaalsele arendajate ja kasutajate publikule.
Alus: JavaScripti veateade (Error Object) ja try...catch
Enne kui uurime TypeScripti täiustusi, on oluline mõista JavaScripti veakäsitluse alustalasid. Põhimehhanism on Error objekt, mis on aluseks kõikidele standardsetele sisseehitatud vigadele.
Standardsed veatüübid JavaScriptis
Error: Üldine baasvea objekt. Enamik kohandatud vigu laiendab seda.TypeError: Näitab, et operatsioon teostati vale tüüpi väärtusega.ReferenceError: Väljastatakse, kui tehakse kehtetu viide (nt proovitakse kasutada deklareerimata muutujat).RangeError: Näitab, et numbriline muutuja või parameeter on väljaspool oma kehtivat vahemikku.SyntaxError: Ilmneb koodi parsimisel, mis ei ole kehtiv JavaScript.URIError: Väljastatakse, kui funktsioone naguencodeURI()võidecodeURI()kasutatakse valesti.EvalError: Seotud globaalseeval()funktsiooniga (kaasaegses koodis vähem levinud).
Põhilised try...catch plokid
Fundamentaalne viis sünkroonsete vigade käsitlemiseks JavaScriptis (ja TypeScriptis) on try...catch lausega:
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("Division by zero is not allowed.");
}
return a / b;
}
try {
const result = divide(10, 0);
console.log(`Result: ${result}`);
} catch (error) {
console.error("An error occurred:", error);
}
// Väljund:
// An error occurred: Error: Division by zero is not allowed.
Traditsioonilises JavaScriptis oli catch-ploki parameeter vaikimisi any tüüpi. See tähendas, et võisite käsitleda error-it ükskõik millena, mis võis põhjustada potentsiaalseid käitusaja probleeme, kui ootasite kindla kujuga viga, kuid saite midagi muud (nt visati lihtne string või number). See tüübiohutuse puudumine võis muuta veakäsitluse hapraks ja raskesti silutavaks.
TypeScripti evolutsioon: unknown tüüp catch-klauslites
TypeScript 4.4 tulekuga muudeti catch-klausli muutuja tüüp any asemel unknown-iks. See oli oluline edasiminek tüübiohutuse seisukohalt. unknown tüüp sunnib arendajaid vea tüüpi enne sellega töötamist selgesõnaliselt kitsendama. See tähendab, et te ei saa lihtsalt kasutada omadusi nagu error.message või error.statusCode, ilma et oleksite eelnevalt vea tüüpi kontrollinud või kinnitanud. See muudatus peegeldab pühendumust tugevamatele tüübigarantiidele, vältides levinud lõkse, kus arendajad eeldavad vea kuju valesti.
try {
throw "Oops, something went wrong!"; // Visatakse string, mis on JS-is kehtiv
} catch (error) {
// TS 4.4+ versioonis on 'error' tüüpi 'unknown'
// console.log(error.message); // VIGA: 'error' on tüüpi 'unknown'.
}
See rangus on funktsioon, mitte viga. See sunnib meid kirjutama robustsemat veakäsitlusloogikat, pannes aluse tüübiohututele mustritele, mida me järgnevalt uurime.
Miks on vigade tüübiohutus globaalsete rakenduste jaoks ülioluline
Rakenduste puhul, mis teenindavad globaalset kasutajaskonda ja mida arendavad rahvusvahelised meeskonnad, on järjepidev ja ennustatav veakäsitlus esmatähtis. Vigade tüübiohutus pakub mitmeid selgeid eeliseid:
- Parem töökindlus ja stabiilsus: Määratledes selgelt veatüübid, hoiate ära ootamatud käitusaja krahhid, mis võivad tekkida olematute omaduste kasutamisel valesti vormindatud veaobjektil. See viib stabiilsemate rakendusteni, mis on kriitilise tähtsusega teenuste puhul, kus seisakuaeg võib põhjustada märkimisväärseid rahalisi või mainekahjusid erinevatel turgudel.
- Parem arendajakogemus (DX) ja hooldatavus: Kui arendajad mõistavad selgelt, milliseid vigu funktsioon võib visata või tagastada, saavad nad kirjutada sihipärasemat ja tõhusamat käsitlusloogikat. See vähendab kognitiivset koormust, kiirendab arendust ning muudab koodi lihtsamini hooldatavaks ja refaktoreeritavaks, eriti suurtes, hajutatud meeskondades, mis hõlmavad erinevaid ajavööndeid ja kultuurilisi taustu.
- Ennustatav veakäsitlusloogika: Tüübiohutud vead võimaldavad ammendavat kontrolli. Saate kirjutada
switch-lauseid võiif/else ifahelaid, mis katavad kõik võimalikud veatüübid, tagades, et ükski viga ei jää käsitlemata. See ennustatavus on ülioluline süsteemide jaoks, mis peavad järgima rangeid teenusetaseme lepinguid (SLA) või regulatiivseid vastavusstandardeid kogu maailmas. - Parem silumine ja tõrkeotsing: Spetsiifilised veatüübid koos rikkalike metaandmetega pakuvad silumise ajal hindamatut konteksti. Üldise "midagi läks valesti" asemel saate täpset teavet, näiteks
NetworkErrorkoosstatusCode: 503-ga võiValidationErrorkoos kehtetute väljade loendiga. See selgus vähendab drastiliselt probleemide diagnoosimisele kuluvat aega, mis on tohutu eelis operatsioonimeeskondadele, kes töötavad erinevates geograafilistes asukohtades. - Selged API lepingud: API-de või korduvkasutatavate moodulite kujundamisel muutub visatavate vigade tüüpide selgesõnaline mainimine osaks funktsiooni lepingust. See parandab integratsioonipunkte, võimaldades teistel teenustel või meeskondadel teie koodiga ennustatavamalt ja ohutumalt suhelda.
- Hõlbustab veateadete rahvusvahelistamist: Hästi defineeritud veatüüpidega saate kaardistada spetsiifilised veakoodid lokaliseeritud sõnumitele kasutajatele erinevates keeltes ja kultuurides.
UserNotFoundErrorvõib esitada "User not found" inglise keeles, "Utilisateur introuvable" prantsuse keeles või "Usuario no encontrado" hispaania keeles, parandades kasutajakogemust globaalselt ilma aluseks olevat veakäsitlusloogikat muutmata.
Vigade käsitlemisel tüübiohutuse omaksvõtmine on investeering teie rakenduse tulevikku, tagades, et see jääb robustseks, skaleeritavaks ja hallatavaks, kui see areneb ja teenindab globaalset publikut.
Muster 1: Käitusaja tüübikontroll (unknown vigade kitsendamine)
Arvestades, et catch-ploki muutujad on TypeScript 4.4+ versioonis tüüpi unknown, on esimene ja kõige fundamentaalsem muster vea tüübi kitsendamine catch-ploki sees. See tagab, et te kasutate ainult neid omadusi, mis on veaobjektil pärast kontrolli garanteeritult olemas.
instanceof Error kasutamine
Kõige levinum ja otsekohesem viis unknown vea kitsendamiseks on kontrollida, kas see on sisseehitatud Error klassi (või mõne selle tuletatud klassi, nagu TypeError, ReferenceError jne) instants.
function riskyOperation(): void {
// Simuleeri erinevaid veatüüpe
const rand = Math.random();
if (rand < 0.3) {
throw new Error("Generic error occurred!");
} else if (rand < 0.6) {
throw new TypeError("Invalid data type provided.");
} else {
throw { code: 500, message: "Internal Server Error" }; // Mitte-Error objekt
}
}
try {
riskyOperation();
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`Caught an Error object: ${error.message}`);
// Saate kontrollida ka spetsiifilisi Error alamklasse
if (error instanceof TypeError) {
console.error("Specifically, a TypeError was caught.");
}
} else if (typeof error === 'string') {
console.error(`Caught a string error: ${error}`);
} else if (typeof error === 'object' && error !== null && 'message' in error) {
// Käsitse kohandatud objekte, millel on 'message' omadus
console.error(`Caught a custom error object with message: ${(error as { message: string }).message}`);
} else {
console.error("An unexpected type of error occurred:", error);
}
}
See lähenemine pakub põhilist tüübiohutust, võimaldades teil kasutada standardsete Error objektide omadusi message ja name. Spetsiifilisemate veastsenaariumide jaoks soovite aga rikkalikumat teavet.
Kohandatud tüübikaitsed spetsiifilistele veaobjektidele
Sageli defineerib teie rakendus oma kohandatud veastruktuure, mis võivad sisaldada spetsiifilisi veakoode, unikaalseid identifikaatoreid või täiendavaid metaandmeid. Nende kohandatud omaduste ohutuks kasutamiseks saate luua kasutaja defineeritud tüübikaitseid.
// 1. Määratle kohandatud vealiidesed/tüübid
interface NetworkError {
name: "NetworkError";
message: string;
statusCode: number;
url: string;
}
interface ValidationError {
name: "ValidationError";
message: string;
fields: { [key: string]: string };
}
// 2. Loo tüübikaitsed iga kohandatud vea jaoks
function isNetworkError(error: unknown): error is NetworkError {
return (
typeof error === 'object' &&
error !== null &&
'name' in error &&
(error as { name: string }).name === "NetworkError" &&
'message' in error &&
'statusCode' in error &&
'url' in error
);
}
function isValidationError(error: unknown): error is ValidationError {
return (
typeof error === 'object' &&
error !== null &&
'name' in error &&
(error as { name: string }).name === "ValidationError" &&
'message' in error &&
'fields' in error &&
typeof (error as { fields: unknown }).fields === 'object'
);
}
// 3. Näide kasutamisest 'try...catch' plokis
function fetchData(url: string): Promise {
return new Promise((resolve, reject) => {
// Simuleeri API-kõnet, mis võib visata erinevaid vigu
const rand = Math.random();
if (rand < 0.4) {
reject(new Error("Something unexpected happened."));
} else if (rand < 0.7) {
reject({
name: "NetworkError",
message: "Failed to fetch data",
statusCode: 503,
url
} as NetworkError);
} else {
reject({
name: "ValidationError",
message: "Invalid input data",
fields: { 'email': 'Invalid format' }
} as ValidationError);
}
});
}
async function processData() {
const url = "https://api.example.com/data";
try {
const data = await fetchData(url);
console.log("Data fetched successfully:", data);
} catch (error: unknown) {
if (isNetworkError(error)) {
console.error(`Network Error from ${error.url}: ${error.message} (Status: ${error.statusCode})`);
// Spetsiifiline käsitlus võrguprobleemidele, nt uuesti proovimise loogika või kasutaja teavitamine
} else if (isValidationError(error)) {
console.error(`Validation Error: ${error.message}`);
console.error("Invalid fields:", error.fields);
// Spetsiifiline käsitlus valideerimisvigadele, nt vigade kuvamine vormiväljade kõrval
} else if (error instanceof Error) {
console.error(`Standard Error: ${error.message}`);
} else {
console.error("An unknown or unexpected error type occurred:", error);
// Tagavara tõeliselt ootamatute vigade jaoks
}
}
}
processData();
See muster muudab teie veakäsitlusloogika oluliselt robustsemaks ja loetavamaks. See sunnib teid kaaluma ja selgesõnaliselt käsitlema erinevaid veastsenaariume, mis on hooldatavate rakenduste ehitamisel ülioluline.
Muster 2: Kohandatud veaklassid
Kuigi tüübikaitsed liidestel on kasulikud, on struktureeritum ja objektorienteeritum lähenemine kohandatud veaklasside defineerimine. See muster võimaldab teil kasutada pärilust, luues spetsiifiliste veatüüpide hierarhia, mida saab täpselt püüda ja käsitleda instanceof kontrollidega, sarnaselt sisseehitatud JavaScripti vigadele, kuid teie enda kohandatud omadustega.
Sisemise Error klassi laiendamine
Parim praktika kohandatud vigade jaoks TypeScriptis (ja JavaScriptis) on laiendada baasklassi Error. See tagab, et teie kohandatud vead säilitavad omadused nagu message ja stack, mis on silumiseks ja logimiseks üliolulised.
// Kohandatud baasviga
class CustomApplicationError extends Error {
constructor(message: string, public code: string = 'GENERIC_ERROR') {
super(message);
this.name = this.constructor.name; // Määrab vea nimeks klassi nime
// Säilita stack trace paremaks silumiseks
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
}
// Spetsiifilised kohandatud vead
class DatabaseConnectionError extends CustomApplicationError {
constructor(message: string, public databaseName: string, public connectionString?: string) {
super(message, 'DB_CONN_ERROR');
}
}
class UserAuthenticationError extends CustomApplicationError {
constructor(message: string, public userId?: string, public reason: 'INVALID_CREDENTIALS' | 'SESSION_EXPIRED' | 'FORBIDDEN' = 'INVALID_CREDENTIALS') {
super(message, 'AUTH_ERROR');
}
}
class DataValidationFailedError extends CustomApplicationError {
constructor(message: string, public invalidFields: { [key: string]: string }) {
super(message, 'VALIDATION_ERROR');
}
}
Kohandatud veaklasside eelised
- Semantiline tähendus: Veaklasside nimed annavad kohese ülevaate probleemi olemusest (nt
DatabaseConnectionErrorviitab selgelt andmebaasi probleemile). - Laiendatavus: Saate lisada igale veatüübile spetsiifilisi omadusi (nt
statusCode,userId,fields), mis on selle konkreetse vea kontekstis asjakohased, rikastades veainfot silumiseks ja käsitlemiseks. - Lihtne tuvastamine
instanceofabil: Erinevate kohandatud vigade püüdmine ja eristamine muutubinstanceofabil triviaalseks, võimaldades täpset veakäsitlusloogikat. - Hooldatavus: Vigade definitsioonide tsentraliseerimine muudab teie koodibaasi lihtsamini mõistetavaks ja hallatavaks. Kui vea omadused muutuvad, uuendate ühte klassi definitsiooni.
- Tööriistade tugi: IDE-d ja linterid suudavad sageli pakkuda paremaid soovitusi ja hoiatusi eristatavate veaklassidega tegelemisel.
Kohandatud veaklasside käsitlemine
function performDatabaseOperation(query: string): any {
const rand = Math.random();
if (rand < 0.4) {
throw new DatabaseConnectionError("Failed to connect to primary DB", "users_db");
} else if (rand < 0.7) {
throw new UserAuthenticationError("User session expired", "user123", 'SESSION_EXPIRED');
} else {
throw new DataValidationFailedError("User input invalid", { 'name': 'Name is too short', 'email': 'Invalid email format' });
}
}
try {
performDatabaseOperation("SELECT * FROM users");
} catch (error: unknown) {
if (error instanceof DatabaseConnectionError) {
console.error(`Database Error: ${error.message}. DB: ${error.databaseName}. Code: ${error.code}`);
// Loogika uuesti ühendumiseks või operatsioonimeeskonna teavitamiseks
} else if (error instanceof UserAuthenticationError) {
console.warn(`Authentication Error (${error.reason}): ${error.message}. User ID: ${error.userId || 'N/A'}`);
// Loogika sisselogimislehele suunamiseks või loa värskendamiseks
} else if (error instanceof DataValidationFailedError) {
console.error(`Validation Error: ${error.message}. Invalid fields: ${JSON.stringify(error.invalidFields)}`);
// Loogika valideerimissõnumite kuvamiseks kasutajale
} else if (error instanceof Error) {
console.error(`An unexpected standard error occurred: ${error.message}`);
} else {
console.error("A truly unexpected error occurred:", error);
}
}
Kohandatud veaklasside kasutamine tõstab oluliselt teie veakäsitluse kvaliteeti. See võimaldab teil ehitada keerukaid veahaldussüsteeme, mis on nii robustsed kui ka lihtsasti mõistetavad, mis on eriti väärtuslik suurte ja keerulise äriloogikaga rakenduste puhul.
Muster 3: Tulemi/Either monaadi muster (selgesõnaline veakäsitlus)
Kuigi try...catch koos kohandatud veaklassidega pakub robustset erindite käsitlemist, väidavad mõned funktsionaalse programmeerimise paradigmad, et erandid rikuvad normaalset kontrollivoogu ja võivad muuta koodi raskemini mõistetavaks, eriti asünkroonsete operatsioonidega tegelemisel. "Tulemi" või "Either" monaadi muster pakub alternatiivi, muutes edu ja ebaõnnestumise funktsiooni tagastustüübis selgesõnaliseks, sundides kutsujat käsitlema mõlemat tulemust ilma kontrollivoo jaoks try/catch-ile tuginemata.
Mis on tulemi/Either muster?
Selle asemel, et viga visata, tagastab funktsioon, mis võib ebaõnnestuda, spetsiaalse tüübi (sageli nimetatakse Result või Either), mis kapseldab kas eduka väärtuse (Ok või Right) või vea (Err või Left). See muster on levinud keeltes nagu Rust (Result) ja Scala (Either).
Põhiidee on see, et tagastustüüp ise annab teile teada, et funktsioonil on kaks võimalikku tulemust, ja TypeScripti tüübisüsteem tagab, et te käsitlete mõlemat.
Lihtsa Result tüübi implementeerimine
type Result = { success: true; value: T } | { success: false; error: E };
// Abifunktsioonid Ok ja Err tulemuste loomiseks
const ok = (value: T): Result => ({ success: true, value });
const err = (error: E): Result => ({ success: false, error });
interface User {
id: string;
name: string;
email: string;
}
// Selle mustri jaoks kohandatud vead (võib endiselt kasutada klasse)
class UserNotFoundError extends Error {
constructor(userId: string) {
super(`User with ID '${userId}' not found.`);
this.name = 'UserNotFoundError';
}
}
class DatabaseReadError extends Error {
constructor(message: string, public details?: string) {
super(message);
this.name = 'DatabaseReadError';
}
}
// Funktsioon, mis tagastab Result tüübi
function getUserById(id: string): Result {
// Simuleeri andmebaasi operatsiooni
const rand = Math.random();
if (rand < 0.3) {
return err(new UserNotFoundError(id)); // Tagasta vea tulemus
} else if (rand < 0.6) {
return err(new DatabaseReadError("Failed to read from DB", "Connection timed out")); // Tagasta andmebaasi viga
} else {
return ok({
id: id,
name: "John Doe",
email: `john.${id}@example.com`
}); // Tagasta eduka tulemus
}
}
// Result tüübi kasutamine
const userResult = getUserById("user-123");
if (userResult.success) {
console.log(`User found: ${userResult.value.name}, Email: ${userResult.value.email}`);
} else {
// TypeScript teab, et userResult.error on tüüpi UserNotFoundError | DatabaseReadError
if (userResult.error instanceof UserNotFoundError) {
console.error(`Application Error: ${userResult.error.message}`);
// Loogika kasutaja mitte leidmisel, nt sõnumi kuvamine kasutajale
} else if (userResult.error instanceof DatabaseReadError) {
console.error(`System Error: ${userResult.error.message}. Details: ${userResult.error.details}`);
// Loogika andmebaasi probleemi puhul, nt uuesti proovimine või süsteemiadministraatorite teavitamine
} else {
// Ammendav kontroll või tagavara muude potentsiaalsete vigade jaoks
console.error("An unexpected error occurred:", userResult.error);
}
}
See muster võib olla eriti võimas, kui aheldada operatsioone, mis võivad ebaõnnestuda, kuna saate kasutada map, flatMap (või andThen) ja muid funktsionaalseid konstruktsioone Result töötlemiseks ilma selgesõnaliste if/else kontrollideta igal sammul, lükates veakäsitluse ühte punkti.
Tulemi mustri eelised
- Selgesõnaline veakäsitlus: Funktsioonid deklareerivad selgelt, milliseid vigu nad saavad oma tüübi signatuuris tagastada, sundides kutsujat tunnistama ja käsitlema kõiki võimalikke ebaõnnestumise olekuid. See välistab "unustatud" erandid.
- Referentsiaalne läbipaistvus: Vältides erandeid kui kontrollivoo mehhanismi, muutuvad funktsioonid ennustatavamaks ja lihtsamini testitavaks.
- Parem loetavus: Edu ja ebaõnnestumise kooditee on selgelt piiritletud, muutes loogika jälgimise lihtsamaks.
- Kompositsioonilisus: Tulemi tüübid sobivad hästi kokku funktsionaalse programmeerimise tehnikatega, võimaldades elegantset vigade levitamist ja teisendamist.
try...catchkoodikorduse puudumine: Paljudes stsenaariumides võib see muster vähendada vajadusttry...catchplokkide järele, eriti mitme ebaõnnestuva operatsiooni koostamisel.
Kaalutlused ja kompromissid
- Sõnaohkrus: Võib olla sõnaohtram lihtsate operatsioonide puhul või kui funktsionaalseid konstruktsioone tõhusalt ei kasutata.
- Õppimiskõver: Arendajad, kes on uued funktsionaalses programmeerimises või monaadides, võivad seda mustrit esialgu keeruliseks pidada.
- Asünkroonsed operatsioonid: Kuigi see on rakendatav, nõuab integreerimine olemasoleva Promise-põhise asünkroonse koodiga hoolikat mähkimist või teisendamist. Teegid nagu
neverthrowvõifp-tspakuvad keerukamaid `Either`/`Result` implementatsioone, mis on kohandatud TypeScriptile, sageli parema asünkroonse toega.
Tulemi/Either muster on suurepärane valik rakendustele, mis eelistavad selgesõnalist veakäsitlust, funktsionaalset puhtust ja tugevat rõhku tüübiohutusele kõigis täitmisteedes. See sobib eriti hästi missioonikriitiliste süsteemide jaoks, kus iga potentsiaalne ebaõnnestumise režiim peab olema selgesõnaliselt arvesse võetud.
Muster 4: Tsentraliseeritud veakäsitluse strateegiad
Kuigi üksikud `try...catch` plokid ja tulemi tüübid käsitlevad kohalikke vigu, saavad suuremad rakendused, eriti need, mis teenindavad globaalset kasutajaskonda, tohutult kasu tsentraliseeritud veakäsitlusstrateegiatest. Need strateegiad tagavad järjepideva vearaportimise, logimise ja kasutaja tagasiside kogu süsteemis, olenemata sellest, kust viga pärineb.
Globaalsed veakäsitlejad
Veakäsitluse tsentraliseerimine võimaldab teil:
- Logida vigu järjepidevalt monitooringusüsteemi (nt Sentry, Datadog).
- Pakkuda tundmatute vigade jaoks üldisi, kasutajasõbralikke veateateid.
- Käsitleda kogu rakendust hõlmavaid muresid, nagu teadete saatmine, tehingute tagasivõtmine või kaitselülitite käivitamine.
- Tagada, et PII (isiklikult tuvastatav teave) või tundlikud andmed ei satuks kasutajatele või logidesse veateadetesse, rikkudes andmekaitse-eeskirju (nt GDPR, CCPA).
Taustarakenduse (Node.js/Express) näide
Node.js Express rakenduses saate defineerida veakäsitluse vahevara, mis püüab kõik teie marsruutide ja muude vahevarade poolt visatud vead. See vahevara peaks olema viimane registreeritud vahevara.
import express, { Request, Response, NextFunction } from 'express';
// Oletame, et need on meie kohandatud veaklassid
class APIError extends Error {
constructor(message: string, public statusCode: number = 500) {
super(message);
this.name = 'APIError';
}
}
class UnauthorizedError extends APIError {
constructor(message: string = 'Unauthorized') {
super(message, 401);
this.name = 'UnauthorizedError';
}
}
class BadRequestError extends APIError {
constructor(message: string = 'Bad Request') {
super(message, 400);
this.name = 'BadRequestError';
}
}
const app = express();
app.get('/api/users/:id', (req: Request, res: Response, next: NextFunction) => {
const userId = req.params.id;
if (userId === 'admin') {
return next(new UnauthorizedError('Access denied for admin user.'));
}
if (!/^[a-z0-9]+$/.test(userId)) {
return next(new BadRequestError('Invalid user ID format.'));
}
// Simuleeri edukat operatsiooni või teist ootamatut viga
const rand = Math.random();
if (rand < 0.5) {
// Edukas kasutaja toomine
res.json({ id: userId, name: 'Test User' });
} else {
// Simuleeri ootamatut sisemist viga
next(new Error('Failed to retrieve user data due to an unexpected issue.'));
}
});
// Tüübiohutu veakäsitluse vahevara
app.use((err: unknown, req: Request, res: Response, next: NextFunction) => {
// Logi viga sisemiseks monitooringuks
console.error(`[ERROR] ${new Date().toISOString()} - ${req.method} ${req.originalUrl} -`, err);
if (err instanceof APIError) {
// Spetsiifiline käsitlus teadaolevatele API vigadele
return res.status(err.statusCode).json({
status: 'error',
message: err.message,
code: err.name // Või spetsiifiline rakenduse defineeritud veakood
});
} else if (err instanceof Error) {
// Üldine käsitlus ootamatutele standardvigadele
return res.status(500).json({
status: 'error',
message: 'An unexpected server error occurred.',
// Tootmises vältige detailsete sisemiste veateadete paljastamist klientidele
detail: process.env.NODE_ENV === 'development' ? err.message : undefined
});
} else {
// Tagavara tõeliselt tundmatute veatüüpide jaoks
return res.status(500).json({
status: 'error',
message: 'An unknown server error occurred.',
detail: process.env.NODE_ENV === 'development' ? String(err) : undefined
});
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Näited cURL käskudest:
// curl http://localhost:3000/api/users/admin
// curl http://localhost:3000/api/users/invalid-id!
// curl http://localhost:3000/api/users/valid-id
Esirakenduse (React) näide: veapiirid (Error Boundaries)
Esirakenduse raamistikes nagu React pakuvad veapiirid (Error Boundaries) viisi JavaScripti vigade püüdmiseks oma alamkomponentide puus, nende vigade logimiseks ja tagavara kasutajaliidese kuvamiseks, selle asemel, et kogu rakendus kokku jookseks. TypeScript aitab defineerida nende piiride propse ja olekut ning kontrollida veaobjekti tüüpi.
import React, { Component, ErrorInfo, ReactNode } from 'react';
interface ErrorBoundaryProps {
children: ReactNode;
fallback?: ReactNode; // Valikuline kohandatud tagavara kasutajaliides
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
errorInfo: ErrorInfo | null;
}
class AppErrorBoundary extends Component {
public state: ErrorBoundaryState = {
hasError: false,
error: null,
errorInfo: null,
};
// Seda staatilist meetodit kutsutakse pärast seda, kui alamkomponent on vea visanud.
static getDerivedStateFromError(_: Error): ErrorBoundaryState {
// Uuenda olekut, et järgmine renderdus näitaks tagavara kasutajaliidest.
return { hasError: true, error: _, errorInfo: null };
}
// Seda meetodit kutsutakse pärast seda, kui alamkomponent on vea visanud.
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// Siin saate vea logida ka vearaportimisteenusesse
console.error("Uncaught error in AppErrorBoundary:", error, errorInfo);
this.setState({ errorInfo: errorInfo, error: error });
}
public render() {
if (this.state.hasError) {
// Saate renderdada mis tahes kohandatud tagavara kasutajaliidest
if (this.props.fallback) {
return this.props.fallback;
}
return (
Oops! Midagi läks valesti.
Vabandame ebamugavuste pärast. Palun proovige lehte värskendada või võtke ühendust toega.
{this.state.error && (
Vea üksikasjad
{this.state.error.message}
{this.state.errorInfo && (
Komponendi pinu:
{this.state.errorInfo.componentStack}
)}
)}
);
}
return this.props.children;
}
}
// Kuidas seda kasutada:
// function App() {
// return (
//
//
//
// );
// }
Operatiivsete ja programmeerimisvigade eristamine
Tsentraliseeritud veakäsitluse oluline aspekt on kahe peamise veakategooria eristamine:
- Operatiivsed vead: Need on ennustatavad probleemid, mis võivad tekkida normaalse töö käigus ja on sageli väljaspool rakenduse tuumikloogikat. Näideteks on võrguühenduse ajalõpp, andmebaasi ühenduse tõrked, kehtetu kasutajasisend, faili ei leitud või kiiruspiirangud. Neid vigu tuleks käsitleda sujuvalt, sageli tulemuseks on kasutajasõbralikud sõnumid või spetsiifiline uuesti proovimise loogika. Tavaliselt ei viita need vead teie koodis olevale bugile. Kohandatud veaklassid spetsiifiliste veakoodidega on nende jaoks suurepärased.
- Programmeerimisvead: Need on bugid teie koodis. Näideteks on
ReferenceError(määratlemata muutuja kasutamine),TypeError(meetodi kutsuminenull-il) või loogikavead, mis viivad ootamatute olekuteni. Need on tavaliselt käitusajal taastamatud ja nõuavad koodiparandust. Globaalsed veakäsitlejad peaksid neid ulatuslikult logima ja potentsiaalselt käivitama rakenduse taaskäivitamise või teavitused arendusmeeskonnale.
Vigade kategoriseerimisega saab teie tsentraliseeritud käsitleja otsustada, kas kuvada üldine veateade, proovida taastamist või eskaleerida probleem arendajatele. See eristamine on ülioluline tervisliku ja reageerimisvõimelise rakenduse säilitamiseks erinevates keskkondades.
Tüübiohutuse veakäsitluse parimad praktikad
Et maksimeerida TypeScripti eeliseid oma veakäsitlusstrateegias, kaaluge neid parimaid praktikaid:
- Kitsendage alati
unknowncatch-plokkides: Alates TypeScript 4.4+ oncatch-muutujaunknown. Tehke alati käitusaja tüübikontrolle (ntinstanceof Error, kohandatud tüübikaitsed), et turvaliselt kasutada vea omadusi. See hoiab ära levinud käitusaja vead. - Kujundage tähendusrikkaid kohandatud veaklasse: Laiendage baasklassi
Error, et luua spetsiifilisi, semantiliselt rikkaid veatüüpe. Lisage asjakohaseid kontekstipõhiseid omadusi (ntstatusCode,errorCode,invalidFields,userId), et aidata silumisel ja käsitlemisel. - Olge veateadete lepingutes selgesõnaline: Dokumenteerige vead, mida funktsioon võib visata või tagastada. Kui kasutate tulemi mustrit, on see tagatud tagastustüübi signatuuriga.
try/catchpuhul on väärtuslikud selged JSDoc kommentaarid või funktsiooni signatuurid, mis edastavad potentsiaalseid erandeid. - Logige vigu põhjalikult: Kasutage struktureeritud logimise lähenemist. Püüdke kinni kogu vea stack trace koos kohandatud vea omaduste ja kontekstuaalse teabega (nt päringu ID, kasutaja ID, ajatempel, keskkond). Kriitiliste rakenduste puhul integreerige tsentraliseeritud logimis- ja monitooringusüsteemiga (nt ELK Stack, Splunk, DataDog, Sentry).
- Vältige üldiste
stringvõiobjecttüüpide viskamist: Kuigi JavaScript seda lubab, muudab toorete stringide, numbrite või lihtobjektide viskamine tüübiohutu veakäsitluse võimatuks ja viib hapra koodini. Visake alatiError-i või kohandatud veaklasside instantse. - Kasutage
neverammendavaks kontrolliks: Kohandatud veatüüpide uniooniga tegelemisel (ntswitch-lauses võiif/else ifseerias) kasutage tüübikaitset, mis viib viimaseelse-ploki jaoks `never` tüübini. See tagab, et kui lisatakse uus veatüüp, märgib TypeScript käsitlemata jäänud juhu. - Tõlkige vead kasutajakogemuse jaoks: Sisemised veateated on arendajatele. Lõppkasutajate jaoks tõlkige tehnilised vead selgeteks, tegevusele suunatud ja kultuuriliselt sobivateks sõnumiteks. Kaaluge veakoodide kasutamist, mis kaardistuvad lokaliseeritud sõnumitele, et toetada rahvusvahelistamist.
- Eristage taastatavaid ja taastamatuid vigu: Kujundage oma veakäsitlusloogika nii, et see eristaks vigu, mida saab uuesti proovida või ise parandada (nt võrguprobleemid), ja neid, mis viitavad fataalsele rakenduse veale (nt käsitlemata programmeerimisvead).
- Testige oma veateid: Nii nagu testite õnnelikke teid, testige rangelt ka oma veateid. Veenduge, et teie rakendus käsitleb sujuvalt kõiki oodatud veatingimusi ja ebaõnnestub ennustatavalt, kui tekivad ootamatud tingimused.
type SpecificError = DatabaseConnectionError | UserAuthenticationError | DataValidationFailedError;
function handleSpecificError(error: SpecificError) {
if (error instanceof DatabaseConnectionError) {
// ...
} else if (error instanceof UserAuthenticationError) {
// ...
} else if (error instanceof DataValidationFailedError) {
// ...
} else {
// See rida peaks ideaalis olema kättesaamatu. Kui see on, lisati SpecificError-ile uus veatüüp,
// kuid seda ei käsitletud siin, põhjustades TS-i vea.
const exhaustiveCheck: never = error; // TypeScript märgib selle, kui 'error' ei ole 'never'
}
}
Nende praktikate järgimine tõstab teie TypeScripti rakendused lihtsalt funktsionaalsetest robustseteks, usaldusväärseteks ja väga hooldatavateks, mis on võimelised teenindama erinevaid kasutajaskondi kogu maailmas.
Levinud lõksud ja kuidas neid vältida
Isegi parimate kavatsustega võivad arendajad sattuda levinud lõksudesse TypeScriptis vigade käsitlemisel. Nende lõksude teadvustamine aitab teil neist eemale hoida.
unknowntüübi ignoreeriminecatch-plokkides:Lõks: Otse
error-i tüübi eeldaminecatch-plokis ilma kitsendamiseta.try { throw new Error("Oops"); } catch (error) { // Tüüpi 'unknown' ei saa omistada tüübile 'Error'. // Omadus 'message' ei eksisteeri tüübil 'unknown'. // console.error(error.message); // See on TypeScripti viga! }Vältimine: Kasutage alati
instanceof Errorvõi kohandatud tüübikaitseid tüübi kitsendamiseks.try { throw new Error("Oops"); } catch (error: unknown) { if (error instanceof Error) { console.error(error.message); } else { console.error("A non-Error type was thrown:", error); } }catch-plokkide liigne üldistamine:Lõks:
Error-i püüdmine, kui kavatsete käsitleda ainult spetsiifilist kohandatud viga. See võib varjata aluseks olevaid probleeme.// Oletame kohandatud APIError class APIError extends Error { /* ... */ } function fetchData() { throw new APIError("Failed to fetch"); } function processData() { try { fetchData(); } catch (error: unknown) { // See püüab APIError-i, aga ka *kõiki* teisi Error-eid, mis võivad olla visatud // fetchData või muu koodi poolt try-plokis, varjates potentsiaalselt buge. if (error instanceof Error) { console.error("Caught a generic error:", error.message); } } }Vältimine: Olge nii spetsiifiline kui võimalik. Kui ootate spetsiifilisi kohandatud vigu, püüdke need esimesena. Kasutage tagavara üldise
Error-i võiunknown-i jaoks.try { fetchData(); } catch (error: unknown) { if (error instanceof APIError) { // Käsitle APIError-it spetsiifiliselt console.error("API Error:", error.message); } else if (error instanceof Error) { // Käsitle teisi standardseid vigu console.error("Unexpected standard Error:", error.message); } else { // Käsitle tõeliselt tundmatuid vigu console.error("Truly unexpected error:", error); } }- Spetsiifiliste veateadete ja konteksti puudumine:
Lõks: Üldiste sõnumite viskamine nagu "Tekkis viga" ilma kasuliku konteksti pakkumata, mis muudab silumise keeruliseks.
throw new Error("Something went wrong."); // Pole eriti abiksVältimine: Veenduge, et veateated on kirjeldavad ja sisaldavad asjakohaseid andmeid (nt parameetrite väärtused, failiteed, ID-d). Kohandatud veaklassid spetsiifiliste omadustega on selleks suurepärased.
throw new DatabaseConnectionError("Failed to connect to DB", "users_db", "mongodb://localhost:27017"); - Kasutajale suunatud ja sisemiste vigade mitteeristamine:
Lõks: Toorete tehniliste veateadete (nt stack trace, andmebaasi päringu vead) kuvamine otse lõppkasutajatele.
// Halb: Sisemiste detailide paljastamine kasutajale catch (error: unknown) { if (error instanceof Error) { res.status(500).send(`Server Error
${error.stack}
`); } }Vältimine: Tsentraliseerige veakäsitlus, et kinni püüda sisemised vead ja tõlkida need kasutajasõbralikeks, lokaliseeritud sõnumiteks. Logige tehnilised üksikasjad ainult arendajatele.
// Hea: Kasutajasõbralik sõnum kliendile, detailne logi arendajatele catch (error: unknown) { // ... logimine arendajatele ... res.status(500).send("Vabandame!
Tekkis ootamatu viga. Palun proovige hiljem uuesti.
"); } - Veaobjektide muteerimine:
Lõks:
errorobjekti otsene muutmine `catch` ploki sees, eriti kui see seejärel uuesti visatakse või edastatakse teisele käsitlejale. See võib põhjustada ootamatuid kõrvalmõjusid või algse vea konteksti kadumist.Vältimine: Kui teil on vaja viga rikastada, looge uus veaobjekt, mis mähitseb algse, või edastage täiendav kontekst eraldi. Algne viga peaks silumise eesmärgil jääma muutumatuks.
Nende levinud lõksude teadliku vältimisega muutub teie TypeScripti veakäsitlus robustsemaks, läbipaistvamaks ja aitab lõppkokkuvõttes kaasa stabiilsema ja kasutajasõbralikuma rakenduse loomisele.
Kokkuvõte
Tõhus veakäsitlus on professionaalse tarkvaraarenduse nurgakivi ja TypeScript tõstab selle kriitilise distsipliini uutesse kõrgustesse. Omaksvõttes tüübiohutuid veakäsitlusmustreid, saavad arendajad liikuda reaktiivsest vigade parandamisest proaktiivse süsteemidisaini juurde, ehitades rakendusi, mis on olemuselt vastupidavamad, ennustatavamad ja hooldatavamad.
Oleme uurinud mitmeid võimsaid mustreid:
- Käitusaja tüübikontroll: Turvaline
unknownvigade kitsendaminecatch-plokkides, kasutadesinstanceof Errorja kohandatud tüübikaitseid, et tagada vea omaduste ennustatav kasutus. - Kohandatud veaklassid: Semantiliste veatüüpide hierarhia kujundamine, mis laiendavad baas-
Error-it, pakkudes rikkalikku kontekstuaalset teavet ja hõlbustades täpset käsitlemistinstanceofkontrollidega. - Tulemi/Either monaadi muster: Alternatiivne funktsionaalne lähenemine, mis kodeerib selgesõnaliselt edu ja ebaõnnestumise funktsiooni tagastustüüpidesse, sundides kutsujaid käsitlema mõlemat tulemust ja vähendades sõltuvust traditsioonilistest erandimehhanismidest.
- Tsentraliseeritud veakäsitlus: Globaalsete veakäsitlejate (nt vahevara, veapiirid) rakendamine, et tagada järjepidev logimine, monitooring ja kasutaja tagasiside kogu rakenduses, eristades operatiivseid ja programmeerimisvigu.
Iga muster pakub unikaalseid eeliseid ja optimaalne valik sõltub sageli konkreetsest kontekstist, arhitektuuristiilist ja meeskonna eelistustest. Kuid ühine joon kõigis neis lähenemistes on pühendumus tüübiohutusele. TypeScripti range tüübisüsteem toimib võimsa kaitsjana, suunates teid robustsemate veateadete lepingute poole ja aidates teil tabada potentsiaalseid probleeme kompileerimisajal, mitte käitusajal.
Nende strateegiate kasutuselevõtt on investeering, mis tasub end ära rakenduse stabiilsuses, arendajate produktiivsuses ja üldises kasutajate rahulolus, eriti tegutsedes dünaamilises ja mitmekesises globaalses tarkvaramaastikul. Alustage nende tüübiohutute veakäsitlusmustrite integreerimist oma TypeScripti projektidesse juba täna ja ehitage rakendusi, mis seisavad kindlalt vastu digitaalmaailma vältimatutele väljakutsetele.